home *** CD-ROM | disk | FTP | other *** search
- /*
- * writefil.c - Pcal routines concerned with writing the PostScript output
- *
- * Contents:
- *
- * def_footstring
- * expand_fmt
- * find_daytext
- * find_noteboxes
- * print_colors
- * print_dates
- * print_db_word
- * print_julian_info
- * print_month
- * print_moon_info
- * print_pstext
- * print_text
- * print_word
- * select_color
- * set_rgb
- * write_calfile
- * write_psfile
- *
- * Revision history:
- *
- * 4.5 AWR 04/05/94 select real vs. dummy PostScript code
- * within write_psfile() (cf. pcalinit.ps)
- *
- * 11/30/93 pre-scale all fonts used (as suggested
- * by Andrew Houghton; cf. pcalinit.ps)
- *
- * 11/24/93 replace find_holidays() with
- * print_colors() (cf. drawnums{} in
- * pcalinit.ps)
- *
- * 11/16/93 Add set_rgb() to handle red:green:blue
- * values for date/fill colors
- *
- * 09/23/93 Support both ROMAN-8 and LATIN-8 font
- * mappings
- *
- * AWR 07/09/93 Revised PostScript comment block
- *
- * AWR 03/01/93 add optional mapping of 8-bit fonts
- *
- * AWR 02/05/93 Support -# flag (multiple copies of
- * each output page)
- *
- * AWR 04/22/92 use STRSIZ for temp buffer size
- *
- * 4.4 AWR 04/07/92 revise to use new PUTCHAR and PUTSTR
- * macros (cf. pcaldefs.h)
- *
- * 01/20/92 support -z and revised -[bgGO] flags
- *
- * AWR 01/13/92 support optional font size in -d and
- * -t flags; move initialization of fonts
- * and sizes here (from pcalinit.ps)
- *
- * 4.3 AWR 12/03/91 add support for -s flag (specify
- * alternate date/fill box shading values)
- *
- * 4.2 AWR 10/08/91 add support for -[kK] flags (change
- * position of small calendars)
- *
- * 10/03/91 add find_noteboxes(); revise to print
- * text in multiple notes boxes
- *
- * add support for -S flag
- *
- * 10/02/91 modify def_footstring() to handle all
- * types of strings; use it to print notes
- * header (-N flag)
- *
- * 09/19/91 add write_calfile(), print_dates(),
- * and new print_text() to generate
- * input for Un*x "calendar" utility;
- * renamed old print_text() as
- * print_pstext() for clarity; revised
- * to simplify setting working date
- *
- * 4.11 AWR 08/23/91 revise expand_fmt() to write results
- * to string instead of stdout; revise
- * print_word() to avoid writing null
- * strings
- *
- * AWR 08/21/91 use ABBR_DAY_LEN and ABBR_MONTH_LEN
- * (cf. pcallang.h) to print abbreviated
- * day/month names
- *
- * AWR 08/21/91 add %u and %w (calculate week number
- * so that 1/1 is always week 1); support
- * %[+-]<n>[DWMY] to adjust working date
- * by +|- <n> days/weeks/months/years
- *
- * 4.1 AWR 08/16/91 Support -G flag (outlined gray dates)
- *
- * 4.02 AWR 07/02/91 Added "%" expansions in text strings
- * (cf. expand_fmt())
- *
- * 4.0 AWR 01/28/91 Support -B, -w flags and moon file
- *
- * 01/15/91 Extracted from pcal.c
- *
- */
-
- /*
- * Standard headers:
- */
-
- #include <stdio.h>
- #include <ctype.h>
- #include <string.h>
- #include <time.h>
-
- /*
- * Pcal-specific definitions:
- */
-
- #include "pcaldefs.h"
- #include "pcalglob.h"
- #define WRITEFIL /* to get ordinal_suffix() from pcallang.h */
- #include "pcallang.h"
- #include "pcalinit.h" /* PostScript boilerplate */
-
- /*
- * Macros:
- */
-
- /* make sure PRT() doesn't round "ph" up to 1.0 when printing it */
- #define PRT_TWEAK(ph) ((ph) >= 0.9995 ? 0.0 : (ph))
-
- /* advance working date by n days */
- #define SET_DATE(n) do { \
- MAKE_DATE(date, work_month, work_day + (n), work_year); \
- normalize(&date); \
- work_month = date.mm, work_day = date.dd, work_year = date.yy; \
- } while (0)
-
- /* prescale a font and add its name to list */
- #define ADDFONT(name, size, font, isarray) do { \
- char *p = alloc(strlen(name) + strlen(size) + 2); \
- sprintf(allfonts[nfonts++] = p, "%s_%s", name, size); \
- PRT("/%s { %sfontsize ", p, font); \
- if (isarray) PRT("%s get ", size); \
- PRT("%sfont FontFind } def\n", font); \
- } while (0)
-
- #define GEN_PSCODE(arr) \
- for (ap = arr; *ap; ap++) \
- PRT("%s\n", *ap)
-
- #define FOOTSTRINGS() (lfoot[0] || cfoot[0] || rfoot[0])
-
- /* various PostScript comments and commands for start/end of page */
-
- #ifdef EPS
-
- /* macro definitions for generating EPS output */
-
- #define PS_STARTPAGE() do { \
- page++; \
- PRT("%%%%Page: %d %d\n", page, page); \
- PRT("%%%%BeginPageSetup\n"); \
- PRT("clear flush\n"); \
- PRT("/PageNum { %d } def\n", page); \
- PRT("/PageState save def\n"); \
- PRT("%%%%EndPageSetup\n"); \
- } while (0)
-
- #define PS_ENDPAGE() do { \
- PRT("%%%%PageTrailer\n"); \
- PRT("showpage\n"); \
- PRT("clear flush\n"); \
- PRT("PageState restore\n"); \
- } while (0)
-
- #define PS_TRAILER() do { \
- PRT("%%%%Trailer\n"); \
- PRT("clear flush\n"); \
- PRT("%%%%EOF\n"); \
- } while (0)
-
- #else /* non-EPS flavors of above macros */
- #define PS_STARTPAGE()
- #define PS_ENDPAGE() PRT("showpage\n");
- #define PS_TRAILER()
- #endif
-
- /* reset working date to original date */
- #define RESET_DATE() \
- work_month = this_month, work_day = this_day, work_year = this_year
-
- #define NEWFONT "-8" /* suffix for new 8-bit fonts */
-
- #define MAXFONT 20 /* maximum number of fonts to prescale */
-
- /*
- * Globals:
- */
-
- /* order of following strings must conform to #define's in pcaldefs.h (q.v.) */
- static char *cond[3] = {"false", "true", "(some)"};
-
- static int this_day, this_month, this_year; /* current day */
- static int work_day, work_month, work_year; /* working day (cf. expand_fmt()) */
- static char *kw_note, *kw_opt, *kw_year; /* keywords for -c output */
-
- static int debug_text; /* generate debug output */
-
-
- /*
- * write_psfile - write PostScript code
- *
- * The actual output of the PostScript code is straightforward. This routine
- * writes a PostScript header followed by declarations of all the PostScript
- * variables affected by command-line flags and/or language dependencies. It
- * the generates the PostScript boilerplate generated from pcalinit.ps, and
- * finally calls print_month() to generate the PostScript code for each
- * requested month.
- *
- */
- void
- #ifdef PROTOS
- write_psfile(int month, /* starting month */
- int year, /* starting year */
- int nmonths) /* number of months */
- #else
- write_psfile(month, year, nmonths)
- int month; /* starting month */
- int year; /* starting year */
- int nmonths; /* number of months */
- #endif
- {
- int i, nfonts, nfsize, copies, dfltsize, color_dates;
- char *p, **ap, tmp[STRSIZ], alt_color, *allfonts[MAXFONT];
- struct tm *p_tm;
- time_t t;
- #ifdef UN_X
- struct passwd *pw;
- #endif
- /* default date, title, weekday font sizes (small/medium/large) */
- static int dsize[3] = DATEFONTSIZE;
- static int tsize[3] = TITLEFONTSIZE;
- static int wsize[3] = WEEKDAYFONTSIZE;
- static int fsize[3] = FOOTFONTSIZE;
- static int dmargin[3] = DATEMARGIN;
- static double gwidth[3] = GRIDLINEWIDTH;
- static char *calsize[3] = CALSIZE;
-
- debug_text = DEBUG(DEBUG_TEXT); /* debug text output? */
-
- /*
- * Write out PostScript prolog (including version/datefile stamp)
- */
-
- /* comment block at top */
-
- PRT("%%!%s\n", PS_RELEASE); /* PostScript release */
-
-
- PRT("%%%%CreationDate: "); /* date/time stamp */
- p_tm = localtime((time(&t), &t));
- if (date_style == EUR_DATES)
- PRT("%d.%d.", p_tm->tm_mday, p_tm->tm_mon + 1);
- else
- PRT("%d/%d/", p_tm->tm_mon + 1, p_tm->tm_mday);
- PRT("%02d %02d:%02d:%02d\n", p_tm->tm_year % 100, p_tm->tm_hour,
- p_tm->tm_min, p_tm->tm_sec);
- PRT("%%%%Creator: %s %s %s\n", CREATED_MSG, progname, version);
-
- #ifdef UN_X
- if ((pw = getpwuid(getuid())) != NULL) {
- strcpy(tmp, pw->pw_gecos);
- if ((p = strchr(tmp, ',')) != NULL)
- *p = '\0';
- PRT("%%%%For: %s\n", pw->pw_name);
- PRT("%%%%Routing: %s\n", tmp);
- }
- #endif
-
- /* Identify the output (month/year range and input file) */
-
- if (do_whole_year && month == JAN) {
- PRT("%%%%Title: %s %d", TITLE_MSG, year);
- if (nmonths > 12)
- PRT(" - %d", year + ((nmonths - 1) / 12));
- } else {
- char c = date_style == EUR_DATES ? '.' : '/';
- PRT("%%%%Title: %s %d%c%02d", TITLE_MSG, month, c, year % 100);
- if (nmonths > 1)
- PRT(" - %d%c%02d", (month + (nmonths - 1) - 1) % 12 + 1,
- c, (year + (nmonths - 1 + month - 1) / 12) % 100);
- }
- if (*datefile)
- PRT(DATEFILE_MSG, datefile);
- PRT("\n");
-
- /* Miscellaneous other identification */
-
- PRT("%%%%Pages: %d\n", do_whole_year ? nmonths / 12 : nmonths);
- PRT("%%%%PageOrder: Ascend\n");
- PRT("%%%%Orientation: %s\n", rotate == LANDSCAPE ? "Landscape" :
- "Portrait");
- PRT("%%%%BoundingBox: 0 0 612 792\n");
- PRT("%%%%ProofMode: NotifyMe\n");
- PRT("%%%%EndComments\n\n");
-
- /* number of copies (from -#<n> flag) */
-
- if ((copies = atoi(ncopy)) > MAXCOPY)
- copies = MAXCOPY; /* save a tree */
- if (copies > 1)
- PRT("/#copies %d def\n", copies);
-
- /* calendar sizes: to minimize number of pre-scaled fonts, whole-
- * year calendars define 'medium' as 0 and the other sizes as -1
- * (not used); single-month calendars define 'large' as 0, 'small'
- * as 1, and 'medium' as -1 (not used)
- */
-
- for (i = SMALL; i <= LARGE; i++)
- PRT("/%s %d def\n", calsize[i],
- do_whole_year ? (i == MEDIUM ? 0 : -1) :
- (i == MEDIUM ? -1 : i == SMALL));
-
- /* font names and sizes */
-
- /* v4.4 supports user override of note and large date/title sizes */
- nfsize = (p = strrchr(notesfont, '/')) ? *p++ = '\0', atoi(p) :
- atoi(strrchr(NOTESFONT, '/') + 1);
- dsize[LARGE] = (p = strrchr(datefont, '/')) ? *p++ = '\0', atoi(p) :
- atoi(strrchr(DATEFONT, '/') + 1);
- tsize[LARGE] = (p = strrchr(titlefont, '/')) ? *p++ = '\0', atoi(p) :
- atoi(strrchr(TITLEFONT, '/') + 1);
-
- /* enlarge footer strings in whole-year/portrait mode */
- if (do_whole_year && rotate == PORTRAIT)
- fsize[MEDIUM] *= 1.25;
-
- /*
- * if 8-bit remapping has been requested (-r flag), create new fonts
- * with desired character remapping (Roman8 or Latin1, as requested)
- */
-
- if (mapfonts != NOMAP) {
-
- /* include desired mapping */
- GEN_PSCODE(mapfonts == ROMAN8 ? ps_roman8 : ps_latin1);
-
- /* boilerplate to remap for 8-bit fonts */
- GEN_PSCODE(ps_remap);
-
- /* always generate code to remap title font */
- PRT("/%s /%s%s remap_font\n", titlefont, titlefont, NEWFONT);
- strcat(titlefont, NEWFONT);
-
- #if MAP_DATEFONT /* any text printed in date font (cf. pcaldefs.h)? */
- /* generate code to remap date font if necessary */
- PRT("FontDirectory /%s%s known not {\n", datefont, NEWFONT);
- PRT("/%s /%s%s remap_font\n", datefont, datefont, NEWFONT);
- PRT("} if\n");
- strcat(datefont, NEWFONT);
- #endif
-
- /* generate code to remap notes font if necessary */
- PRT("FontDirectory /%s%s known not {\n", notesfont, NEWFONT);
- PRT("/%s /%s%s remap_font\n", notesfont, notesfont, NEWFONT);
- PRT("} if\n");
- strcat(notesfont, NEWFONT);
- }
-
- /* define title, notes, and date fonts */
- PRT("/titlefont /%s def\n", titlefont);
- PRT("/datefont /%s def\n", datefont);
- PRT("/notesfont /%s def\n", notesfont);
-
- /* typically defined in terms of above fonts - must define last */
- PRT("/weekdayfont %s def\n", WEEKDAYFONT);
- PRT("/footfont %s def\n", FOOTFONT);
- PRT("/headingfont %s def\n", HEADINGFONT);
-
- /* print various font sizes and line/margin widths as PostScript arrays:
- * one element for whole-year calendars; two (large, small) for single-
- * month calendars
- */
-
- if (do_whole_year) {
- PRT("/datemargin [ %d ] def\n", dmargin[MEDIUM]);
- PRT("/gridlinewidth [ %.1f ] def\n", gwidth[MEDIUM]);
- PRT("/titlefontsize [ %d ] def\n", tsize[MEDIUM]);
- PRT("/datefontsize [ %d ] def\n", dsize[MEDIUM]);
- } else {
- PRT("/datemargin [ %d %d ] def\n", dmargin[LARGE], dmargin[SMALL]);
- PRT("/gridlinewidth [ %.1f %.1f ] def\n", gwidth[LARGE], gwidth[SMALL]);
- PRT("/titlefontsize [ %d %d ] def\n", tsize[LARGE], tsize[SMALL]);
- PRT("/datefontsize [ %d %d ] def\n", dsize[LARGE], dsize[SMALL]);
- }
-
- dfltsize = do_whole_year ? MEDIUM : LARGE;
-
- PRT("/weekdayfontsize %d def\n", wsize[dfltsize]);
- PRT("/footfontsize %d def\n", fsize[dfltsize]);
- PRT("/notesfontsize %d def\n", nfsize);
- PRT("/headingfontsize %d def\n", HEADINGFONTSIZE);
-
- /* pre-scale all fonts used by PostScript code; try to be smart about
- * skipping those that we know (at this point) won't be needed (whole-
- * year calendars use either 3 or 4 fonts, while single-month calendars
- * can use anywhere from 3 to 8). "FF" et. al. are indices into the
- * font array (cf pcalinit.ps) for the different font types.
- */
-
- PRT("/FontFind { findfont exch scalefont } def\n");
-
- nfonts = 0;
-
- PRT("/FF %d def\n", nfonts); /* footers */
- if (FOOTSTRINGS())
- ADDFONT("ff", calsize[dfltsize], "foot", FALSE);
-
- if (do_whole_year) {
- PRT("/TF %d def\n", nfonts); /* month/year title */
- ADDFONT("tf", calsize[MEDIUM], "title", TRUE);
-
- PRT("/DF %d def\n", nfonts); /* dates */
- ADDFONT("df", calsize[MEDIUM], "date", TRUE);
- } else {
- PRT("/HF %d def\n", nfonts); /* 'Notes' heading */
- if (notes_hdr[0])
- ADDFONT("hf", calsize[LARGE], "heading", FALSE);
-
- /* large/small (if used) scalings of the same font must be
- * contiguous and appear in that order
- */
-
- PRT("/TF %d def\n", nfonts); /* large/small title */
- ADDFONT("tf", calsize[LARGE], "title", TRUE);
- if (small_cal_pos != SC_NONE)
- ADDFONT("tf", calsize[SMALL], "title", TRUE);
-
- PRT("/DF %d def\n", nfonts); /* large/small dates */
- ADDFONT("df", calsize[LARGE], "date", TRUE);
- if (small_cal_pos != SC_NONE)
- ADDFONT("df", calsize[SMALL], "date", TRUE);
-
- PRT("/NF %d def\n", nfonts); /* text in boxes */
- ADDFONT("nf", calsize[LARGE], "notes", FALSE);
- }
-
- PRT("/WF %d def\n", nfonts); /* weekdays */
- ADDFONT("wf", calsize[dfltsize], "weekday", FALSE);
-
- /* generate the font array (automatically in sync with above) */
-
- PRT("/allfonts [\n\t");
- for (i = 0; i < nfonts; i++) {
- PRT("%s ", allfonts[i]);
- free(allfonts[i]);
- }
- PRT("\n] def\n");
-
- /*
- * define various strings and numeric values used by Pcal
- */
-
- /* month names */
-
- PRT("/month_names [");
- for (i = JAN; i <= DEC; i++) {
- PRT(i % 6 == 1 ? "\n\t" : " ");
- (void) print_word(months[i-1]);
- }
- PRT(" ] def\n");
-
- /* day names - abbreviate if printing entire year on page */
-
- PRT("/day_names [");
- for (i = SUN; i <= SAT; i++) {
- PRT(i % 6 == 0 && ! do_whole_year ? "\n\t" : " ");
- strcpy(tmp, days[(i + first_day_of_week) % 7]);
- if (do_whole_year)
- tmp[ABBR_DAY_LEN] = '\0';
- (void) print_word(tmp);
- }
- PRT(" ] def\n");
-
- /* line separator */
-
- PRT("/linesep ");
- print_word(LINE_SEP);
- PRT(" def\n");
-
- /* rotation, scale, and translate values */
-
- PRT("/rval %d def\n", rotate);
- PRT("/xsval %s def\n/ysval %s def\n", xsval, ysval);
- PRT("/xtval %s def\n/ytval %s def\n", xtval, ytval);
-
- /* moon, Julian date, and box fill flags */
-
- PRT("/draw-moons %s def\n", cond[draw_moons]);
- PRT("/julian-dates %s def\n", cond[julian_dates]);
- PRT("/fill-boxes %s def\n", cond[! blank_boxes]);
-
- /* position of small calendars */
-
- PRT("/prev_small_cal %d def\n", prev_cal_box[small_cal_pos]);
- PRT("/next_small_cal %d def\n", next_cal_box[small_cal_pos]);
-
- /* date and fill box shading values */
-
- strcpy(tmp, shading);
- *(p = strchr(tmp, '/')) = '\0';
- PRT("/dategray %s def\n", set_rgb(tmp));
- PRT("/fillgray %s def\n", set_rgb(p + 1));
- color_dates = strchr(tmp, RGB_CHAR) != NULL;
-
- /* PostScript boilerplate (part 1 of 1) */
-
- GEN_PSCODE(header);
-
- /* Additional PostScript code tailored to this calendar */
-
- GEN_PSCODE(color_dates ?
- ps_prtday_rgb : /* color prtday{} */
- ps_prtday_bw); /* B&W prtday{} */
-
- GEN_PSCODE(FOOTSTRINGS() ?
- ps_footer : /* at least one foot string */
- ps_nofooter); /* no foot strings */
-
- GEN_PSCODE(blank_boxes ?
- ps_nofill : /* blank fill boxes */
- ps_fill); /* shaded fill boxes */
-
- if (do_whole_year) {
- GEN_PSCODE(rotate == LANDSCAPE ?
- ps_year_l : /* medium months (landscape) */
- ps_year_p); /* medium months (portrait) */
- GEN_PSCODE(ps_nojulians); /* no julians or moons */
- GEN_PSCODE(ps_nomoons);
- } else {
- GEN_PSCODE(julian_dates == NO_JULIANS ?
- ps_nojulians : /* no julian dates */
- ps_julians); /* some julian dates */
- GEN_PSCODE(draw_moons == NO_MOONS ?
- ps_nomoons : /* no moons */
- ps_moons); /* some or all moons */
- if (head)
- GEN_PSCODE(ps_text); /* date text */
- GEN_PSCODE(ps_month); /* single month */
- }
-
- /*
- * Write out PostScript code to print calendars
- */
-
- for (this_month = month, this_year = year; nmonths--; ) {
- print_month(this_month, this_year);
- this_year = NEXT_YEAR(this_month, this_year);
- this_month = NEXT_MONTH(this_month, this_year);
- }
-
- /* generate trailer at end of PostScript output */
- PS_TRAILER();
- }
-
-
- /*
- * write_calfile - write dates in format suitable for Un*x "calendar" utility
- * (and subsequent use by Pcal)
- */
- void
- #ifdef PROTOS
- write_calfile(int month, /* starting month */
- int year, /* starting year */
- int nmonths) /* number of months */
- #else
- write_calfile(month, year, nmonths)
- int month /* starting month */;
- int year; /* starting year */
- int nmonths; /* number of months */
- #endif
- {
- KWD *k;
-
- /* look up the Pcal keywords (assumed present) for the -c output file */
- for (k = keywds; k->name; k++) {
- if (k->code == DT_NOTE) kw_note = k->name;
- if (k->code == DT_OPT) kw_opt = k->name;
- if (k->code == DT_YEAR) kw_year = k->name;
- }
-
- /* print the date style for subsequent use by Pcal */
- PRT("%s -%c\n", kw_opt, date_style == USA_DATES ? F_USA_DATES :
- F_EUR_DATES);
-
- for (this_month = month, this_year = year; nmonths--; ) {
- print_dates(this_month, this_year);
- this_year = NEXT_YEAR(this_month, this_year);
- this_month = NEXT_MONTH(this_month, this_year);
- }
- }
-
- /*
- * low-level utilities for PostScript generation
- */
-
- /*
- * set_rgb - convert "<r>:<g>:<b>" to [r g b] or "<gray>" to [gray];
- * return pointer to static buffer containing converted string
- */
- char *
- #ifdef PROTOS
- set_rgb(char *s)
- #else
- set_rgb(s)
- char *s;
- #endif
- {
- static char buf[STRSIZ];
- char *p1, *p2;
- double val[3];
- int n;
-
- val[0] = val[1] = val[2] = 0; /* defaults */
-
- /* extract 1 - 3 floating-point values from string */
- for (n = 1, p1 = s; n <= 3; n++, p1 = p2 + 1) {
- val[n-1] = atof(p1);
- if ((p2 = strchr(p1, RGB_CHAR)) == NULL)
- break;
- }
-
- /* single value is gray scale; assume anything else is [r g b] */
- if (n > 1)
- sprintf(buf, "[%.3f %.3f %.3f]", val[0], val[1], val[2]);
- else
- sprintf(buf, "[%.3f]", val[0]);
-
- return buf;
- }
-
- /*
- * select_color - if the holiday color has not been explicitly selected,
- * choose a color which contrasts with the majority of weekday colors
- */
- int
- #ifdef PROTOS
- select_color(void)
- #else
- select_color()
- #endif
- {
- int i, min, index;
- char count[NUM_COLORS];
-
- for (i = 0; i < NUM_COLORS; i++) /* clear counts */
- count[i] = 0;
-
- for (i = SUN; i <= SAT; i++) /* count colors */
- count[day_color[i]]++;
-
- /* find smallest non-zero count; set its index and value */
- for (i = 0, min = 99; i < NUM_COLORS; i++)
- if (count[i] && count[i] < min)
- min = count[index = i];
-
- /* return least-used color (or pick one if only one color used) */
- return min == 7 ? index == BLACK ? GRAY : BLACK : index;
- }
-
-
- /*
- * expand_fmt - expand a strftime-like date format specifier; pcal supports
- * %[aAbBdjmUWyY] from strftime() plus %[luwDM] and prefixes [0o+-] (see
- * below); places expanded string in output buffer and returns pointer to
- * character following end of format specifier. Assumes working date has
- * been initialized (via RESET_DATE() macro) prior to first call for a given
- * text string
- */
- char *
- #ifdef PROTOS
- expand_fmt(char *buf, /* output buffer (filled in) */
- char *p) /* character following percent sign */
- #else
- expand_fmt(buf, p)
- char *buf; /* output buffer (filled in) */
- char *p; /* character following percent sign */
- #endif
- {
- char c;
- static char *prefixes = "0o+-";
- int firstday, wkday;
- int adjust = 0, print_lz = FALSE, ordinal = FALSE, prev_num = -1;
- int num_present = FALSE, num_value = 0;
- DATE date;
-
- /* For compatibility with version 4.1, still support %[+-][bBdmY]
- * (print the next/last month-name/day/month/year). Version 4.11
- * introduces %[+-]<n>[DWMY], which adjusts the working date by
- * [+-]<n> days/weeks/months/years; this method is preferred due
- * to its greater flexibility.
- */
-
- buf[0] = '\0'; /* initialize output to null string */
-
- do { /* loop until format character found */
- switch (c = *p++) {
- case 'a': /* %a : abbreviated weekday */
- case 'A': /* %A : full weekday */
- wkday = calc_weekday(work_month, work_day, work_year);
- strcpy(buf, days[wkday]);
- if (c == 'a')
- buf[ABBR_DAY_LEN] = '\0';
- break;
-
- case 'b': /* %b : abbreviated month name */
- case 'B': /* %B : full month name */
- strcpy(buf, months[(work_month + adjust + 11) % 12]);
- if (c == 'b')
- buf[ABBR_MONTH_LEN] = '\0';
- break;
-
- case 'd': /* %d : day of month (01-31) */
- prev_num = work_day;
- sprintf(buf, print_lz ? "%02d" : "%d", prev_num);
- break;
-
- case 'D': /* %D : adjust working date by <N> days (NEW) */
- if (!num_present || num_value == 0)
- RESET_DATE();
- else
- SET_DATE(adjust * num_value);
- break;
-
- case 'j': /* %j : day of year (001-366) */
- prev_num = DAY_OF_YEAR(work_month, work_day,
- work_year);
- sprintf(buf, print_lz ? "%03d" : "%d", prev_num);
- break;
-
- case 'l': /* %l : days left in year (000-365) (NEW) */
- prev_num = YEAR_LEN(work_year) - DAY_OF_YEAR(work_month,
- work_day, work_year);
- sprintf(buf, print_lz ? "%03d" : "%d", prev_num);
- break;
-
- case 'm': /* %m : month (01-12) */
- prev_num = (work_month + adjust + 11) % 12 + 1;
- sprintf(buf, print_lz ? "%02d" : "%d", prev_num);
- break;
-
- case 'M': /* %M : adjust date by <N> months (NEW) */
- if (!num_present || num_value == 0)
- RESET_DATE();
- else {
- int len;
-
- work_month += adjust * num_value;
- while (work_month > DEC) {
- work_month -= 12;
- work_year++;
- }
- while (work_month < JAN) {
- work_month += 12;
- work_year--;
- }
-
- /* make sure day of new month is legal */
- len = LENGTH_OF(work_month, work_year);
- if (work_day > len)
- work_day = len;
- }
- break;
-
- /* %u considers the week containing 1/1 to be week 1 and
- * the next "logical Sunday" (the first day of the week
- * as printed - cf. the -F option) to be the start of week
- * 2; %U considers the first "logical Sunday" of the year to
- * be the start of week 1. %w and %W behave like %u and %U
- * respectively, but use the first "logical Monday" instead.
- */
- case 'W': /* %W : week number (00-53) */
- /* %W, if prefaced by [+-]N, adjusts the date by
- * [+-]N weeks (resets if N == 0); check for this
- * case first
- */
- if (num_present) {
- if (num_value == 0) /* N = 0: reset date */
- RESET_DATE();
- else
- SET_DATE(7 * adjust * num_value);
- break;
- }
- /* fall through */
- case 'u': /* %u : week number (01-54) (NEW) */
- case 'U': /* %U : week number (00-53) */
- case 'w': /* %w : week number (01-54) (NEW) */
- firstday = ((TOLOWER(c) == 'w' ? 15 : 14) -
- START_BOX(JAN, work_year)) % 7 + 1;
- prev_num = (DAY_OF_YEAR(work_month, work_day,
- work_year) - firstday + 7) / 7;
- if (islower(c) && firstday != 1)
- prev_num++;
- sprintf(buf, print_lz ? "%02d" : "%d", prev_num);
- break;
-
- case 'y': /* %y : year w/o century (00-99) */
- prev_num = (work_year + adjust) % 100;
- sprintf(buf, "%02d", prev_num);
- break;
-
- case 'Y': /* %Y : year w/century */
- /* %Y, if prefaced by [+-]N, adjusts the date by
- * [+-]N years (resets if N == 0); check for this
- * case first
- */
- if (num_present) {
- if (num_value == 0) /* N = 0: reset date */
- RESET_DATE();
- else {
- int len;
-
- work_year += adjust * num_value;
-
- /* make sure day is legal */
- len = LENGTH_OF(work_month, work_year);
- if (work_day > len)
- work_day = len;
- }
- } else {
- prev_num = work_year + adjust;
- sprintf(buf, "%d", prev_num);
- }
- break;
-
- /* prefix flags [o0+-] : set flags for next pass */
-
- case 'o': /* %o : ordinal suffix (NEW) */
- ordinal = TRUE;
- break;
-
- case '0': /* %0 : add leading zeroes (NEW) */
- print_lz = TRUE;
- break;
-
- case '+': /* %+ : increment next value (NEW) */
- case '-': /* %- : decrement next value (NEW) */
- adjust = c == '-' ? -1 : 1;
- if (isdigit(*p)) { /* get the number */
- num_present = TRUE;
- while (isdigit(*p))
- num_value = num_value * 10 +
- (*p++ - '0');
- }
- break;
-
- case '\0': /* accidental end-of-string */
- case ' ':
- return p - 1;
-
- default: /* other - just copy it to output */
- sprintf(buf, "%c", c);
- break;
- };
-
- } while (strchr(prefixes, c) != NULL);
-
- /* append ordinal suffix if requested */
- if (ordinal && prev_num >= 0)
- strcat(buf, ordinal_suffix(prev_num));
- return p;
-
- }
-
-
- /*
- * print_word - print a single word, representing punctuation and non-ASCII
- * characters as octal literals and expanding format specifiers; return pointer
- * to character following word (NULL if no word follows)
- */
- char *
- #ifdef PROTOS
- print_word(char *p)
- #else
- print_word(p)
- char *p;
- #endif
- {
- char c, buf[STRSIZ];
- int first = TRUE; /* flag to avoid printing null strings */
-
- if (*p == '\0' || *(p += strspn(p, WHITESPACE)) == '\0')
- return NULL;
-
- while ((c = *p) && !isspace(c & CHAR_MSK)) {
- if (c == '%' && p[1] != '\0') {
- p = expand_fmt(buf, p + 1);
- if (*buf && first) {
- PRT("(");
- first = FALSE;
- }
- PUTSTR(isalnum, buf, stdout);
- } else {
- if (first)
- PRT("(");
- first = FALSE;
- PUTCHAR(isalnum, c, stdout);
- p++;
- }
- }
-
- if (!first)
- PRT(")");
-
- return p;
- }
-
-
- /*
- * print_db_word - debug version of print_word; omits parentheses, does not
- * convert punctuation to escape sequences, and writes results to stderr
- * (not stdout)
- */
- char *
- #ifdef PROTOS
- print_db_word(char *p)
- #else
- print_db_word(p)
- char *p;
- #endif
- {
- char c, buf[STRSIZ];
-
- if (*p == '\0' || *(p += strspn(p, WHITESPACE)) == '\0')
- return NULL;
-
- while ((c = *p) && !isspace(c & CHAR_MSK)) {
- if (c == '%' && p[1] != '\0') {
- p = expand_fmt(buf, p + 1);
- PUTSTR(isprint, buf, stderr);
- } else {
- PUTCHAR(isprint, c, stderr);
- p++;
- }
- }
-
- return p;
- }
-
-
- /*
- * print_pstext - print tokens in text (assumed separated by single blank)
- * in PostScript format and as debugging information if requested
- */
- void
- #ifdef PROTOS
- print_pstext(char *p)
- #else
- print_pstext(p)
- char *p;
- #endif
- {
- char *s = p; /* save for possible second pass */
-
- while (p = print_word(p))
- PRT(" ");
-
- /* repeat to generate debugging info if requested */
- if (debug_text)
- while (s = print_db_word(s))
- FPR(stderr, " ");
-
- }
-
-
- /*
- * print_text - print text as supplied; expand format specifiers but
- * do not tokenize into words or translate punctuation to escape sequences
- */
- void
- #ifdef PROTOS
- print_text(char *p)
- #else
- print_text(p)
- char *p;
- #endif
- {
- char c, buf[STRSIZ];
-
- while (c = *p)
- if (c == '%' && p[1] != '\0') {
- p = expand_fmt(buf, p + 1);
- PUTSTR(isprint, buf, stdout);
- } else {
- PUTCHAR(isprint, c, stdout);
- p++;
- }
- }
-
-
- /*
- * def_footstring - print definition for foot string, again converting
- * all characters other than letters, digits, or space to octal escape
- * and expanding format specifiers
- */
- void
- #ifdef PROTOS
- def_footstring(char *p, /* definition */
- char *str) /* name of string */
- #else
- def_footstring(p, str)
- char *p; /* definition */
- char *str; /* name of string */
- #endif
- {
- char c, buf[STRSIZ];
-
- this_day = 1; /* set default day in foot string */
- RESET_DATE(); /* reset working date */
-
- PRT("/%s (", str);
- while (c = *p)
- if (c == '%' && p[1] != '\0') {
- p = expand_fmt(buf, p + 1);
- PUTSTR(isalnum, buf, stdout);
- } else {
- PUTCHAR(isalnum, c, stdout);
- p++;
- }
- PRT(") def\n");
- }
-
-
- /*
- * Routines to extract and print data
- */
-
-
- /*
- * find_daytext - find and print the day (including notes) or holiday text
- * for the specified month/year
- */
- void
- #ifdef PROTOS
- find_daytext(int month,
- int year,
- int is_holiday) /* TRUE: print holiday text */
- #else
- find_daytext(month, year, is_holiday)
- int month, year;
- int is_holiday; /* TRUE: print holiday text */
- #endif
- {
- register int day;
- year_info *py;
- month_info *pm;
- register day_info *pd;
- int first;
- char *fcn = is_holiday ? "holidaytext" : "daytext";
- char hol = is_holiday ? '*' : ' ';
-
- /* if no text for this year and month, return */
-
- if ((py = find_year(year, FALSE)) == NULL ||
- (pm = py->month[month-1]) == NULL)
- return;
-
- /* walk array of day text pointers and linked lists of text */
-
- for (day = 1; day <= LAST_NOTE_DAY; day++) {
- for (pd = pm->day[day-1], first = TRUE;
- pd;
- pd = pd->next) {
- if (pd->is_holiday != is_holiday)
- continue;
- if (first) {
- PRT("%d [ \n", day >= FIRST_NOTE_DAY ?
- note_box(month, day, year) : day);
- }
- else {
- PRT("\n");
- print_word(LINE_SEP); /* separate lines */
- PRT("\n");
- }
- this_day = day >= FIRST_NOTE_DAY ? 1 : day;
- RESET_DATE(); /* reset working date */
- if (debug_text)
- if (day < FIRST_NOTE_DAY)
- FPR(stderr, "%02d/%02d/%d%c ", month,
- day, year, hol);
- else
- FPR(stderr, "%02d[%02d]%d ", month,
- day - FIRST_NOTE_DAY + 1, year);
- print_pstext(pd->text);
- if (debug_text)
- FPR(stderr, "\n");
- first = FALSE;
- }
- if (! first) { /* wrap up call (if one made) */
- PRT("\n] %s\n", day >= FIRST_NOTE_DAY ? "notetext" :
- fcn);
- }
- }
- }
-
-
- /*
- * print_colors - print array specifying color of each date in current month
- * (formerly calculated on the fly in drawnums{} in pcalinit.ps)
- */
- void
- #ifdef PROTOS
- print_colors(int month,
- int year)
- #else
- print_colors(month, year)
- int month, year;
- #endif
- {
- register int day;
- year_info *py;
- month_info *pm;
- unsigned long holidays;
- int i, j, len;
- short color[32];
-
- len = LENGTH_OF(month, year);
-
- /* determine each date's color from its day of the week */
- for (day = 1, j = FIRST_OF(month, year);
- day <= len;
- day++, j = (j + 1) % 7) {
- color[day] = day_color[j];
- }
-
- pm = (py = find_year(year, FALSE)) ? py->month[month-1] : NULL;
-
- /* override weekday color for holidays */
- for (holidays = pm ? pm->holidays : 0, day = 1;
- holidays;
- holidays >>= 1, day++)
- if (holidays & 01)
- color[day] = holiday_color;
-
- PRT("/date_color [ -1"); /* dummy value for element 0 */
- for (day = 1; day <= len; day++)
- PRT("%s %d", day % 10 == 1 ? " " : "", color[day]);
- PRT(" ] def\n");
- }
-
-
- /*
- * find_noteboxes - find and print the note box numbers for specified
- * month/year
- */
- void
- #ifdef PROTOS
- find_noteboxes(int month,
- int year)
- #else
- find_noteboxes(month, year)
- int month, year;
- #endif
- {
- register int day;
- year_info *py;
- month_info *pm;
-
- /* if no text for this year and month, print empty list and return */
-
- if ((py = find_year(year, FALSE)) == NULL ||
- (pm = py->month[month-1]) == NULL) {
- PRT("/noteboxes [ ] def\n");
- return;
- }
-
- PRT("/noteboxes ["); /* start definition of list */
-
- /* walk array of note text pointers, converting days to box numbers */
-
- for (day = FIRST_NOTE_DAY; day <= LAST_NOTE_DAY; day++)
- if (pm->day[day-1])
- PRT(" %d", note_box(month, day, year));
- PRT(" ] def\n");
-
- }
-
-
- /*
- * print_dates - print the dates (in "calendar" format) for the specified
- * month and year
- */
- void
- #ifdef PROTOS
- print_dates(int month,
- int year)
- #else
- print_dates(month, year)
- int month, year;
- #endif
- {
- register int day;
- year_info *py;
- month_info *pm;
- register day_info *pd;
- unsigned long holidays;
- int has_holiday_text;
- static int first = TRUE;
- static int save_year = 0;
-
- /* if no text for this year and month, return */
-
- if ((py = find_year(year, FALSE)) == NULL ||
- (pm = py->month[month-1]) == NULL)
- return;
-
- /* print the year if it has changed */
-
- if (year != save_year)
- PRT("%s %d\n", kw_year, save_year = year);
-
- /* walk array of day text pointers and linked lists of text */
-
- for (holidays = pm->holidays, day = 1;
- day < FIRST_NOTE_DAY;
- holidays >>= 1, day++) {
- has_holiday_text = FALSE;
- for (pd = pm->day[day-1]; pd; pd = pd->next) {
- if (date_style == USA_DATES)
- PRT("%02d/%02d", month, day);
- else
- PRT("%02d/%02d", day, month);
- PRT(pd->is_holiday ? "* " : " ");
- this_day = day;
- RESET_DATE(); /* reset working date */
- print_text(pd->text);
- PRT("\n");
- has_holiday_text |= pd->is_holiday;
- }
- /* was date flagged as holiday w/o associated text? */
- if ((holidays & 01) && !has_holiday_text) {
- if (date_style == USA_DATES)
- PRT("%02d/%02d*\n", month, day);
- else
- PRT("%02d/%02d*\n", day, month);
- }
- }
- }
-
-
-
- /*
- * print_moon_info - print the information necessary to draw moons. If
- * printing moons on all days, print the phase for each day; if printing
- * only quarter moons, tweak the phase to an exact quarter (so the icon
- * is printed correctly) and generate a list of the quarter-moon dates
- */
- void
- #ifdef PROTOS
- print_moon_info(int month,
- int year)
- #else
- print_moon_info(month, year)
- int month, year;
- #endif
- {
- int n, ndays, day, quarter;
- char *p;
- unsigned long qdays;
- double phase;
- static char *q[4] = {"NM", "1Q", "FM", "3Q"};
-
- if (draw_moons == NO_MOONS)
- return;
-
- /* print the phase of the moon for each day of the month */
-
- PRT("/moon_phases [\t\t%% from ");
- if ((p = find_moonfile(year)) != NULL)
- PRT ("%s", p);
- else {
- PRT("algorithm");
- if (atof(time_zone) != 0.0)
- PRT(" (UTC offset = %s)", time_zone);
- }
- PRT("\n\t");
-
- for (n = 0, qdays = 0L, day = 1, ndays = LENGTH_OF(month, year);
- day <= ndays;
- day++) {
- phase = find_phase(month, day, year, &quarter);
- if (DEBUG(DEBUG_MOON))
- FPR(stderr, "%02d/%02d/%d: %.3f %s\n", month, day,
- year, phase, quarter != MOON_OTHER ?
- q[quarter] : "");
- /* adjust phase to exact quarter if printing only quarters */
- if (draw_moons == SOME_MOONS && quarter != MOON_OTHER)
- phase = 0.25 * quarter;
- if (draw_moons == ALL_MOONS || quarter != MOON_OTHER)
- PRT("%.3f%s", PRT_TWEAK(phase), ++n % 10 == 0 ?
- "\n\t" : " ");
- if (quarter != MOON_OTHER)
- qdays |= 1L << (day - 1);
- }
- PRT("] def\n");
-
- /* if drawing only quarter moons, print days when they occur */
-
- if (draw_moons == SOME_MOONS) {
- PRT("/quarter_moons [ ");
- for (day = 1; qdays; day++, qdays >>= 1)
- if (qdays & 01)
- PRT("%d ", day);
- PRT("] def\n");
- }
- }
-
-
- /*
- * print_julian_info - print the information necessary to print Julian dates
- */
- void
- #ifdef PROTOS
- print_julian_info(int month,
- int year)
- #else
- print_julian_info(month, year)
- int month, year;
- #endif
- {
-
- if (julian_dates != NO_JULIANS)
- PRT("/jdstart %d def\n", DAY_OF_YEAR(month, 1, year));
- if (julian_dates == ALL_JULIANS)
- PRT("/yearlen %d def\n", YEAR_LEN(year));
- }
-
-
- /*
- * print_month - generate calendar for specified month/year
- */
- void
- #ifdef PROTOS
- print_month(int month,
- int year)
- #else
- print_month(month, year)
- int month, year;
- #endif
- {
- static int nmonths = 0, page = 0;
- int startbox;
-
- /* start of new physical page? */
- if (!do_whole_year || nmonths % 12 == 0)
- PS_STARTPAGE();
-
- PRT("/year %d def\n", year); /* set up year and month */
- PRT("/month %d def\n", month);
-
- /* move starting box to second row if conflict with small calendars */
- startbox = START_BOX(month, year);
- if (!do_whole_year &&
- (prev_cal_box[small_cal_pos] == startbox ||
- next_cal_box[small_cal_pos] == startbox) )
- startbox += 7;
- PRT("/startbox %d def\n", startbox);
- PRT("/ndays %d def\n", LENGTH_OF(month, year));
-
- find_noteboxes(month, year); /* make list of note boxes */
- print_colors(month, year); /* make list of date colors */
-
- /* are we printing 12 months per page or only one? */
-
- if (do_whole_year) {
- /* reset foot strings at start of each page */
- if (nmonths % 12 == 0) {
- def_footstring(lfoot, "Lfootstring");
- def_footstring(cfoot, "Cfootstring");
- def_footstring(rfoot, "Rfootstring");
- def_footstring(notes_hdr, "notesheading");
- }
-
- PRT("/posn %d def\n", nmonths % 12); /* location on page */
- PRT("printmonth\n");
- }
- else {
- /* reset foot strings each month (may change) */
- def_footstring(lfoot, "Lfootstring");
- def_footstring(cfoot, "Cfootstring");
- def_footstring(rfoot, "Rfootstring");
- def_footstring(notes_hdr, "notesheading");
-
- /* generate information necessary for small calendars */
-
- if (small_cal_pos != SC_NONE) {
- int m, y;
-
- PRT("/p_year %d def\n", y = PREV_YEAR(month, year));
- PRT("/p_month %d def\n", m = PREV_MONTH(month, year));
- PRT("/p_startbox %d def\n", START_BOX(m, y));
- PRT("/p_ndays %d def\n", LENGTH_OF(m, y));
-
- PRT("/n_year %d def\n", y = NEXT_YEAR(month, year));
- PRT("/n_month %d def\n", m = NEXT_MONTH(month, year));
- PRT("/n_startbox %d def\n", START_BOX(m, y));
- PRT("/n_ndays %d def\n", LENGTH_OF(m, y));
- }
-
- print_julian_info(month, year); /* Julian date info */
- print_moon_info(month, year); /* moon info */
-
- PRT("printmonth\n");
- find_daytext(month, year, TRUE); /* holiday text */
- find_daytext(month, year, FALSE); /* day and note text */
- }
-
- /* end of physical page? */
- if (!do_whole_year || ++nmonths % 12 == 0)
- PS_ENDPAGE();
- }
-